// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include <cassert>
#include <QString>
#include <QDomElement>
#include <map>
#include <string>
#include "boost/any.hpp"
#include "boost/lexical_cast.hpp"

namespace OpenMeca
{
  namespace Util
  {

    class TypeXmlSerializerManagerBase
    {
    public:
      TypeXmlSerializerManagerBase(){}
      virtual ~TypeXmlSerializerManagerBase(){}
      virtual void ReadQDomElement(const QDomElement& e, const QString xmlKey)=0;
      virtual void WriteQDomElement(QDomElement& e, const QString xmlKey) const =0;
    };

    template<typename T>
    class TypeXmlSerializerManager: public TypeXmlSerializerManagerBase
    {
    public:
      TypeXmlSerializerManager();
      TypeXmlSerializerManager(const TypeXmlSerializerManager&);
      TypeXmlSerializerManager(T*);
      TypeXmlSerializerManager(T*, const T);
      ~TypeXmlSerializerManager();
      void ReadQDomElement(const QDomElement& e, const QString xmlKey);
      void WriteQDomElement(QDomElement& e, const QString xmlKey) const;
      
    private:
      T* value_;
      const bool hasDefaultValue_;
      const T defaultValue_;
    };
    
    template<typename T>
    inline 
    TypeXmlSerializerManager<T>::TypeXmlSerializerManager()
      :TypeXmlSerializerManagerBase(),
       value_(0),
       hasDefaultValue_(false),
       defaultValue_()
    {
    }

    template<typename T>
    inline 
    TypeXmlSerializerManager<T>::TypeXmlSerializerManager(const TypeXmlSerializerManager& t)
      :TypeXmlSerializerManagerBase(),
       value_(t.value_),
       hasDefaultValue_(t.hasDefaultValue_),
       defaultValue_(t.defaultValue_)
    {
    }
      
    template<typename T>
    inline 
    TypeXmlSerializerManager<T>::TypeXmlSerializerManager(T* val)
      :TypeXmlSerializerManagerBase(),
       value_(val),
       hasDefaultValue_(false),
       defaultValue_()
    {
    }

    template<typename T>
    inline 
    TypeXmlSerializerManager<T>::TypeXmlSerializerManager(T* val, T defaultVal)
      :TypeXmlSerializerManagerBase(),
       value_(val),
       hasDefaultValue_(true),
       defaultValue_(defaultVal)
    {
    }
    
    template<typename T>
    inline 
    TypeXmlSerializerManager<T>::~TypeXmlSerializerManager()
    {
    } 
    


    template<typename T>
    inline void
    TypeXmlSerializerManager<T>::ReadQDomElement(const QDomElement& e, const QString xmlKey)
    { 
      QString valStr = e.attribute(xmlKey);
      if (valStr==QString())
	{
	  if (hasDefaultValue_)
	    {
	      *value_ = defaultValue_;
	      return;
	    }
	  assert(0); //No defaut value && no tag find in xml file
	}
      try
        {
	  *value_ = boost::lexical_cast<T>(valStr.toStdString());
        }
      catch(boost::bad_lexical_cast &)
        {
	  assert(0);
        }
    }
    
    template<typename T>
    inline void
    TypeXmlSerializerManager<T>::WriteQDomElement(QDomElement& e, const QString xmlKey) const
    {
      try
        {
	  std::string str = boost::lexical_cast<std::string>(*value_);
	  e.setAttribute (xmlKey, str.c_str());
        }
      catch(boost::bad_lexical_cast &)
        {
	  assert(0);
        }
     
    }

    class XmlSerializer
    {

    public:
      ~XmlSerializer();
      XmlSerializer();
      template<typename T>void Add(const QString key, T& value);
      template<typename T>void Add(const QString key, T& value, const T& defaultValue);
      void ReadQDomElement(const QDomElement& e);
      void WriteQDomElement(QDomElement& e) const;
    private :
      //Copy constructor and assignment operator are built by compiler

    private:
      std::map<QString,TypeXmlSerializerManagerBase*> map_;
    };

    template<typename T>
    inline void 
    XmlSerializer::Add(const QString key, T& value)
    {
      assert(map_.count(key)==0);
      map_[key]==new TypeXmlSerializerManager<T>(&value);
    }
    
    template<typename T>
    inline void 
    XmlSerializer::Add(const QString key, T& value, const T& defaultValue)
    {
      assert(map_.count(key)==0);
      map_[key]= new TypeXmlSerializerManager<T>(&value, defaultValue);
    }
    
    
  }
}
